package upload

import (
	"common/macaroon"
	"common/rpc"
	rs2 "common/rs"
	"context"
	"euplink/center_rpc"
	"euplink/encrypt_key"
	"euplink/progress"
	"euplink/stream"
	"euplink/upload/upClient"
	"fmt"
	"go.uber.org/zap"
	"io"
	"time"
)

type Uploader struct {
	apiKey      *macaroon.APIKey
	Dialer      *rpc.TcpDialer
	segmentSize int
	bucket      string
	streamId    []byte
	centerRpc   *center_rpc.CenterRpc
	encryptKey  encrypt_key.EncryptKey
	expireTime  time.Time
	Log         *zap.Logger
	Rs          rs2.Rs
	progress    *progress.Progress
}

func NewUploader(apikey *macaroon.APIKey, segmentSize int, dial *rpc.TcpDialer, centerRpc *center_rpc.CenterRpc, encryptKey encrypt_key.EncryptKey, log *zap.Logger) *Uploader {
	return &Uploader{
		apiKey:      apikey,
		segmentSize: segmentSize,
		Dialer:      dial,
		encryptKey:  encryptKey,
		Log:         log,
		centerRpc:   centerRpc,
	}
}

type EncParam struct {
	plainKey   string //原始路径
	encrypKey  []byte //原始路径加密
	derivedKey []byte //派生Key

	plainContentKey  []byte //内容加密用的密码
	encrypContentKey []byte //对plainContentKey 再加密，用于保存数据库
}

func (up *Uploader) Upload(ctx context.Context, bucket, originKey string, inReader io.Reader, plainSize int64, attrs map[string]string) error {
	up.bucket = bucket

	up.Log.Debug("upload ", zap.String("key", originKey))
	var index = 0
	var totalPlainSize int64 = 0
	encKeys, err := up.encryptKey.CreateEncryptKeys(originKey)
	if err != nil {
		return err
	}

	//请求服务器，获取节点信息
	objectBeginResponse, err := up.centerRpc.BeginObject(ctx, center_rpc.BeginObjectParam{
		//PlainObjectKey:      up.encParam.plainKey,
		EncryptedObjectKey:  encKeys.EncryptKey,
		EncryptedContentKey: encKeys.EncryptContentKey,
		Bucket:              up.bucket,
		Attributes:          attrs,
	})
	if err != nil {
		up.Log.Error("BeginObject ", zap.Error(err))
		return err
	}
	//设置动态参数
	up.streamId = objectBeginResponse.StreamId
	up.Rs = rs2.PbToRs(objectBeginResponse.Rs)
	up.Log.Debug("BeginObject 成功", zap.String("streamId", fmt.Sprintf("%x", up.streamId)))

	up.expireTime = time.Time{}
	if objectBeginResponse.ExpireTime > 0 {
		up.expireTime = time.Unix(int64(objectBeginResponse.ExpireTime), 0)
	}

	//f, err := os.Open(inFile)
	//if err != nil {
	//	fmt.Println("Open file ", inFile, err)
	//	return err
	//}
	//defer f.Close()
	//st, _ := f.Stat()

	up.progress = progress.NewProgress(plainSize, int(up.Rs.Total), int(up.Rs.Required))

	cutFileError := stream.CutFile(inReader, int64(up.segmentSize), func(data []byte) error {
		up.progress.SetCurrentSegment(int64(len(data)))
		err := up.uploadSegment(ctx, data, encKeys.PlainContentKey, index)
		up.Log.Info("uploadSegment end", zap.Error(err))
		index++
		totalPlainSize += int64(len(data))
		up.progress.CommitCurrentSegment() //更新进度
		return err
	})
	if cutFileError != nil {
		return cutFileError
	}
	up.Log.Info("CommitObject")
	return up.centerRpc.CommitObject(ctx, up.streamId, encKeys.EncryptKey, up.segmentSize, index, totalPlainSize, up.Rs)
}

func (up *Uploader) uploadSegment(ctx context.Context, data []byte, plainContentKey []byte, segmentIndex int) error {
	//数据加密
	t1 := time.Now()
	pieceData, err := stream.EncodeSegment(up.Rs, data, plainContentKey)
	if err != nil {
		return err
	}
	up.Log.Warn("EncodeSegment use time", zap.Float64("", time.Now().Sub(t1).Seconds()))

	//改为从 BeginSegment 得到 ipv6
	beginRespone, err := up.centerRpc.BeginSegment(ctx, segmentIndex, up.streamId, int32(up.Rs.Total), nil)
	if err != nil {
		up.Log.Error("BeginSegment error", zap.Error(err))
		return err
	}

	//startUploadTime := time.Now()
	//results, uploadErr := up.uploadPiece(ctx, pieceData, segmentIndex, connectInfos)
	store := upClient.NewClient(segmentIndex, up.Log, up.streamId, int(up.Rs.MinUpload), up.expireTime, up.Dialer, up.progress)
	results, uploadErr := store.Upload(ctx, pieceData, beginRespone.Nodes)

	//convertUploadResultStat(up.ApiKey.KeyId(), up.TryCount, time.Now().Sub(startUploadTime), err == nil, results)
	//stat.Report(report)
	if uploadErr != nil {
		return uploadErr
	}
	err = up.centerRpc.CommitSegment(ctx,
		up.streamId,
		pieceData.PaddingLen,
		int32(segmentIndex),
		len(data),
		ConvertUploadResultToStoreInfo(results))

	return err

}

func (up *Uploader) GetPercent() int {
	if up.progress != nil {
		return up.progress.GetRealtimePercent()
	}
	return 0
}
